package marmalius;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.*;

import marmalius.craft.SmithingService;
import marmalius.craft.firemaking.FireMaking;
import marmalius.item.ItemGroundService;
import marmalius.item.ItemService;
import marmalius.object.door.DoorService;
import marmalius.tools.Config;
import marmalius.tools.DBService;
import marmalius.tools.LogService;


public class server implements Runnable {
	private static final server instance = new server();
	
	private int cycleTime;
	public static int ZammyScore = 0;
	public static int SaraScore = 0;
	public static int GuthixScore = 0;
	public int GameTimer = 300;
	
	private long startTime;
	private int upTimeId;
	private int cycleUpTime;
	
	private ServerSocket clientListener = null;
	public static boolean shutdownServer = false;
	public static MagicDelays MagicDelays = null;
	public static ItemHandler itemHandler = null;
	public static Cooking Cooking = null;
	public static ObjectSpawn ObjectSpawn = null;
	public static prayerhandler prayerhandler = null;
	public static ShopHandler shopHandler = null;
	public static Herblore Herblore = null;
	public static MenuHandler MenuHandler = null;
	public static Farming Farming = null;
	public static Fletching Fletching = null;
	public static GraphicsHandler GraphicsHandler = null;
	public static Objects objects = null;
	public static NpcAnimHandler NpcAnimHandler = null;
	public static Woodcutting woodcutting = null;
	public static Mining mining = null;
	
	public static int EnergyRegian = 60;
	public static int MaxConnections = 100;
	public static String[] Connections = new String[MaxConnections];
	public static int[] ConnectionCount = new int[MaxConnections];
	
	public server() { }
	
	public static void main (String [] args) {
		if (!Config.getInstance().loadConfig() ||
			!LogService.getInstance().init(Config.getInstance().getLogLevel(), Config.getInstance().getLogFile(), Config.getInstance().getLogTime()) ||
			!DBService.getInstance().setConfig(Config.getInstance()) ||
			!DBService.getInstance().initSql()) {
			System.out.println("Server Stop");
			return ;
		}
		
		server.getInstance().initServer();
	}
	
	public void initServer() {
		ItemService.getInstance().loadItemTemplate();
		PlayerHandler.getInstance().resetOnlinePlayer();
		ItemGroundService.getInstance().loadItemSpawn();
		DoorService.getInstance().loadDoor();
		
		SmithingService.getInstance().loadSmithing();
		
		NPCHandler.getInstance();
		NpcAnimHandler = new NpcAnimHandler();
		Fletching = new Fletching();
		Farming = new Farming();
		Cooking = new Cooking();
		MenuHandler = new MenuHandler();
		MagicDelays = new MagicDelays();
		ObjectSpawn = new ObjectSpawn();
		Herblore = new Herblore();
		prayerhandler = new prayerhandler();
		
		itemHandler = new ItemHandler();
		shopHandler = new ShopHandler();
		GraphicsHandler = new GraphicsHandler();
		objects = new Objects();
		woodcutting = new Woodcutting();
		mining = new Mining();
		(new Thread(this)).start();
		int waitFails = 0;
		startUpTime();
		long lastTicks = System.currentTimeMillis();
		cycleTime = Config.getInstance().getMaxCycleTime();
		
		while(!shutdownServer) {
			int processCycleTime = (int)(System.currentTimeMillis() - lastTicks);
			lastTicks = System.currentTimeMillis();
			if (DBService.getInstance().isClosed()) {
				LogService.getInstance().logError("SQL Connection Lost");
				killServer();
				return ;
			}
			
			NPCHandler.getInstance().process();
			if(GameTimer > 0)
				GameTimer--;
			else
				GameTimer = 300;
			
			PlayerHandler.getInstance().process(processCycleTime);
			itemHandler.process();
			ItemGroundService.getInstance().process(processCycleTime);
			shopHandler.process();
			prayerhandler.process();
			Cooking.process();
			woodcutting.process();
			FireMaking.getInstance().process(processCycleTime);
			mining.process();
			
			if (cycleUpTime > 120) {
				try {
					long upTime = System.currentTimeMillis() / 1000 - startTime;
					DBService.getInstance().doExecute("UPDATE server_uptime SET uptime="+upTime+" WHERE id = "+upTimeId);
					//good to keep sql connection
				}
		        catch (SQLException ex) {
		        	LogService.getInstance().logError(ex.getMessage());
		        }
		        cycleUpTime = 0;
		        
		        Runtime garbage = Runtime.getRuntime();
		        garbage.gc();
			}
			cycleUpTime++;
			
			long timeSpent = System.currentTimeMillis() - lastTicks;
			if (timeSpent >= cycleTime) {
				timeSpent = cycleTime;
				if (++waitFails > 100)
					LogService.getInstance().logWarn("machine is too slow to run this server!");
			}
			if (cycleTime > timeSpent) {
				try {
					Thread.sleep(cycleTime - timeSpent);
				}
				catch(InterruptedException ex) {
					LogService.getInstance().logError(ex.getMessage());
				}
			}
			
		}
		LogService.getInstance().close();
		System.out.println("main thread down");
	}
	
	public void run() {
		try{
			clientListener = new ServerSocket(Config.getInstance().getServerPort(), 1, null);
			LogService.getInstance().logInfo("Server in listen mode");
			while(!shutdownServer){
				Socket sock = clientListener.accept();
				sock.setTcpNoDelay(true);
				String host = sock.getInetAddress().getHostAddress();
				if (Config.getInstance().getMaxIPPlayer() != 0) {
					int count = PlayerHandler.getInstance().getClientCountByHost(host);
					if (count >= Config.getInstance().getMaxIPPlayer()) {
						sock.close();
						continue;
					}
				}
				PlayerHandler.getInstance().newPlayerClient(sock, host);
			}
		}
		catch(IOException ex) {
			LogService.getInstance().logError(ex.getMessage());
		}
		System.out.println("listen thread down");
	}

	public void killServer() {
		try {
			if(clientListener != null)
				clientListener.close();
			clientListener = null;
		}
		catch(java.lang.Exception __ex) {
			__ex.printStackTrace();
		}
	}

	public Objects getObjects () {
		return objects;
	}

    public ItemHandler getItemHandler () {
        return itemHandler;
    }
	
	private void startUpTime () {
		upTimeId = 0;
		cycleUpTime = 0;
		startTime = System.currentTimeMillis() / 1000;
		try {
			DBService.getInstance().doExecute("INSERT INTO server_uptime (lastup, uptime) VALUES ("+startTime+", 0)");
			upTimeId = DBService.getInstance().getLastInsertId("id");
		}
		catch (SQLException ex) {
			LogService.getInstance().logError(ex.getMessage());
		}
	}
	
	public static server getInstance() { return instance; }
}